<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

</body>

<script>


  let Vuex = {
    store: store
  }

  let VuexStore = Vuex.store({
    state: {
      count: 2,
      price: 100
    },
    getters: {
      getCount: (state) => {
        state.count = 3
        console.log(state.count)
        return state.count
      },
    },
    mutations: {
      increment(state, n) {
        state.count += n
      }
    },
    actions: {
      increment(context, n) {
        context.commit('increment', n)
      }
    },
    watch: {
      count(newVal, oldVal) {
        console.log('新值' + newVal, '旧值' + oldVal)
      }
    }
  })

  VuexStore.commit('increment', 10)
  VuexStore.dispatch('increment', 10)
  VuexStore.getters.getCount



  function store({ state, getters, mutations, actions, watch }) {
    editObjProperty(watch, {
      writable: false,
      enumerable: false
    })

    state = new Proxy(state, {
      get(obj, property) {
        return obj[property]
      },
      set(obj, property, newValue) {
        const oldValue = obj[property]
        obj[property] = newValue

        if (watch[property]) {
          watch[property](newValue, oldValue)
        }
        return true
      }
    })


    editObjProperty(getters, {
      writable: false,
      enumerable: false
    })

    getters = new Proxy(getters, {
      get(obj, property) {
        editObjProperty(state, { writable: false })  // 禁止修改属性值
        let res = obj[property].call(null, state)
        editObjProperty(state, { writable: true }) // 允许修改属性值 

        return deepClone(res)
      },

      set(obj, property, value) {
        return true
      }
    });


    function commit(name, ...param) {
      mutations[name].apply(null, [state, ...param])
    }

    function dispatch(name, ...param) {
      actions[name].apply(null, [{ commit }, ...param])
    }

    function subscribe(name, fn) {
      watch[name] = fn
      editObjProperty(watch, {
        writable: false,
        enumerable: false
      })
    }

    const res = {
      getters,
      commit,
      dispatch,
      subscribe
    }

    editObjProperty(res, {
      writable: false,
      enumerable: false
    })

    return res
  }


  function editObjProperty(state, rule) {
    for (let key in state) {
      Object.defineProperty(state, key, rule)
    }
  }

  function deepClone(value) {
    if (typeof value !== 'object' || obj == null)
      return value

    let container = value instanceof Array ? [] : {}

    for (let key of value) {
      if (value.hasOwnProperty(key)) {
        container[key] = deepClone(value[key])
      }
    }

    return container
  }




</script>

</html>